#ifndef cathlibcpp_memory_H
#define cathlibcpp_memory_H

// File:       memory.h
// Author:     (c) Miles Sabin, 1996
// Purpose:    allocator, runtime_allocator, auto_ptr etc.


#ifndef included_stddef_H
#define included_stddef_H
#include <stddef.h>                // for size_t
#endif

#ifndef cathlibcpp_bool_H
#include "bool.h"
#endif

#ifndef cathlibcpp_config_H
#include "config.h"
#endif

#ifndef cathlibcpp_iterator_H
#include "iterator.h"              // for output_iterator
#endif

#ifndef cathlibcpp_new_H
#include "new.h"
#endif

#ifndef cathlibcpp_newcasts_H
#include "newcasts.h"
#endif

#ifndef cathlibcpp_utility_H
#include "utility.h"               // for pair<T1, T2>
#endif


// default allocator

class allocator
{
  public:

    // constructors
    allocator();
    ~allocator();

    //   mutators
    void* allocate(size_t);
    void deallocate(void*);
};

void* operator new(size_t size, allocator& a);

bool operator==(allocator const&, allocator const&);
bool operator!=(allocator const&, allocator const&);


// base for runtime allocators

class runtime_allocator_impl;

class runtime_allocator
{
  // DWP example has this inheriting from allocator, but I think it must be wrong
  // because if it did it would override non-virtual allocator mfns

  friend bool operator==(runtime_allocator const& lhs, runtime_allocator const& rhs);

  public:

    // constructrors
    runtime_allocator(runtime_allocator const& rhs);
    runtime_allocator& operator=(runtime_allocator const& rhs);

    //   mutators
    void* allocate(size_t size);
    void deallocate(void* p);

    // accessors

  protected:

    runtime_allocator(runtime_allocator_impl* impl);
    ~runtime_allocator();

    runtime_allocator_impl* impl_;
};

void* operator new(size_t size, runtime_allocator& a);

bool operator==(runtime_allocator const& lhs, runtime_allocator const& rhs);

// should be nested in runtime_allocator

class runtime_allocator_impl
{
  public:

    // constructors
    runtime_allocator_impl();
    virtual ~runtime_allocator_impl();

    // mutators
    virtual void* allocate(size_t) = 0;
    virtual void deallocate(void*) = 0;

    void add_reference();
    void remove_reference();

  private:

    int ref_count_;
};


// raw storage iterator

template<class OutputIterator, class T>
class raw_storage_iterator : public output_iterator
{
  public:

    // constructors
    raw_storage_iterator(OutputIterator x)
      : iter_(x)
      {}

    // mutators
    raw_storage_iterator<OutputIterator, T>& operator*()
      { return *this; }

    raw_storage_iterator<OutputIterator, T>& operator=(T const& element)
      { construct(iter_, element); return *this; }

    raw_storage_iterator<OutputIterator, T>& operator++()
      { ++iter_; return *this; }
    raw_storage_iterator<OutputIterator, T> operator++(int)
      {
        raw_storage_iterator<OutputIterator, T> t = *this;
        ++iter_;
        return t;
      }

  private:

    OutputIterator iter_;
};


// memory handling primitives

template<class T>
inline T* allocate(ptrdiff_t n, T*);

template<class T>
inline void deallocate(T* buffer);

template<class T>                 // must be manually specialized for all client classes
inline void destroy(T* t);

template<class T1, class T2>
inline void construct(T1* t, T2 const& value);

template<class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last);

template<class T>
inline pair<T*, ptrdiff_t> get_temporary_buffer(ptrdiff_t n, T*);

template<class T>
inline void return_temporary_buffer(T* p);


// specialized algorithms

template<class InputIterator, class ForwardIterator>
inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result);

template<class ForwardIterator, class T>
inline void uninitialized_fill(ForwardIterator first, ForwardIterator last, T const& x);

template<class ForwardIterator, class Size, class T>
inline void uninitialized_fill_n(ForwardIterator first, Size n, T const& x);


// pointers

template<class T>
class auto_ptr
{
  public:

    // constructors
    auto_ptr(T* p = 0)
      : ptr_(p),
        is_owner_(p != 0)
      {}

    auto_ptr(const auto_ptr<T>& rhs)
      {
        ptr_ = rhs.ptr_;
        is_owner_ = rhs.is_owner_;
        const_cast(auto_ptr<T>&, rhs).is_owner_ = false;
      }

    ~auto_ptr()
      {
        if(is_owner_)
          delete ptr_;
      }

    // accessors
    T& operator*() const
      { return *ptr_; }

#ifndef PROBLEM_MEMBER_ACCESS
    T* operator->() const
      { return ptr_; }
#endif

    T* get() const
      { return ptr_; }

    T* release() const
      {
        const_cast(auto_ptr<T>*, this)->is_owner_ = false;
        return ptr_;
      }

    // mutators
    auto_ptr<T>& operator=(const auto_ptr<T>& rhs)
      {
        if(this != &rhs)
        {
          if(is_owner_)
            delete ptr_;

          ptr_ = rhs.ptr_;
          is_owner_ = rhs.is_owner_;
          const_cast(auto_ptr<T>&, rhs).is_owner_ = false;
        }

        return *this;
      }

  private:

    T* ptr_;
    bool is_owner_;
};


// Implementation of allocator

inline allocator::allocator()
  {}

inline allocator::~allocator()
  {}

inline void* allocator::allocate(size_t size)
  { return ::operator new(size); }

inline void allocator::deallocate(void* p)
  { ::operator delete(p); }


// Implementation of allocator free fns

inline void* operator new(size_t size, allocator& a)
{
  return a.allocate(size);
}

inline bool operator==(allocator const&, allocator const&)
{
  return true;
}

inline bool operator!=(allocator const&, allocator const&)
{
  return false;
}


// Implementation of runtime_allocator::impl

inline runtime_allocator_impl::runtime_allocator_impl()
  : ref_count_(0)
  {}

inline void runtime_allocator_impl::add_reference()
  { ++ref_count_; }

inline void runtime_allocator_impl::remove_reference()
  {
    if(--ref_count_ == 0)
      delete this;
  }


// Implementation of runtime_allocator

inline runtime_allocator::runtime_allocator(runtime_allocator_impl* impl)
  : impl_(impl)
  { impl_->add_reference(); }

inline runtime_allocator::runtime_allocator(runtime_allocator const& rhs)
  : impl_(rhs.impl_)
  { impl_->add_reference(); }

inline runtime_allocator::~runtime_allocator()
  { impl_->remove_reference(); }

inline runtime_allocator& runtime_allocator::operator=(runtime_allocator const& rhs)
  {
    rhs.impl_->add_reference();
    impl_->remove_reference();
    impl_ = rhs.impl_;

    return *this;
  }

inline void* runtime_allocator::allocate(size_t size)
  { return impl_->allocate(size); }

inline void runtime_allocator::deallocate(void* p)
  { impl_->deallocate(p); }


// Implementation of runtime_allocator free fns

inline void* operator new(size_t size, runtime_allocator& a)
{
  return a.allocate(size);
}

inline bool operator==(runtime_allocator const& lhs, runtime_allocator const& rhs)
{
  return lhs.impl_ == rhs.impl_;
}


// Implementation of memory handling primitives

template<class T>
inline T* allocate(ptrdiff_t n, T*)                                // why isn't this size_t n?
  { return reinterpret_cast(T*, ::operator new(n*sizeof(T))); }

template<class T>
inline void deallocate(T* buffer)
  { ::operator delete(buffer); }

template<class T>
inline void destroy(T* t)
{
  t->~T();
}

#ifdef PROBLEM_DESTROY

// instantiate manually

#define TEMPLATE_destroy(T)      \
inline void destroy(T* t)        \
{                                \
  t->~T();                       \
}

#endif

template<class T1, class T2>
inline void construct(T1* t, T2 const& value)
{
  new(t) T1(value);
}

template<class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last)
{
  while(first != last)
    destroy(&*first++);
}

inline void destroy(char*)
{}

inline void destroy(short*)
{}

inline void destroy(int*)
{}

inline void destroy(long*)
{}

inline void destroy(unsigned char*)
{}

inline void destroy(unsigned short*)
{}

inline void destroy(unsigned int*)
{}

inline void destroy(unsigned long*)
{}

inline void destroy(float*)
{}

inline void destroy(double*)
{}

inline void destroy(void**)
{}

inline void destroy(char**)
{}

inline void destroy(short**)
{}

inline void destroy(int**)
{}

inline void destroy(long**)
{}

inline void destroy(unsigned char**)
{}

inline void destroy(unsigned short**)
{}

inline void destroy(unsigned int**)
{}

inline void destroy(unsigned long**)
{}

inline void destroy(float**)
{}

inline void destroy(double**)
{}

inline void construct(char* x, char const& value)
{ *x = value; }

inline void construct(short* x, short const& value)
{ *x = value; }

inline void construct(int* x, int const& value)
{ *x = value; }

inline void construct(long* x, long const& value)
{ *x = value; }

inline void construct(unsigned char* x, unsigned char const& value)
{ *x = value; }

inline void construct(unsigned short* x, unsigned short const& value)
{ *x = value; }

inline void construct(unsigned int* x, unsigned int const& value)
{ *x = value; }

inline void construct(unsigned long* x, unsigned long const& value)
{ *x = value; }

inline void construct(float* x, float const& value)
{ *x = value; }

inline void construct(double* x, double const& value)
{ *x = value; }

inline void construct(void** x, void* value)
{ *x = value; }

template<class T>
inline pair<T*, ptrdiff_t> get_temporary_buffer(ptrdiff_t n, T*)
  {
    T* buffer = 0;
    for(; (buffer = reinterpret_cast(T*, ::operator new(n*sizeof(T), nothrow()))) != 0 && n > 1; n /= 2);
    return make_pair(buffer, n);
  }

template<class T>
inline void return_temporary_buffer(T* p)
  { ::operator delete(p); }


// specialized algorithms

template<class InputIterator, class ForwardIterator>
inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result)
{
  while(first != last)
    construct(&*result++, *first++);
  return result;
}

template<class ForwardIterator, class T>
inline void uninitialized_fill(ForwardIterator first, ForwardIterator last, T const& x)
{
  while(first != last)
    construct(&*first++, x);
}

template<class ForwardIterator, class Size, class T>
inline void uninitialized_fill_n(ForwardIterator first, Size n, T const& x)
{
  while(n--)
    construct(&*first++, x);
}

#endif
